#ifndef __OJ_MODEL_HPP__
#define __OJ_MODEL_HPP__
// OJ题库与数据交互
#include <iostream>
#include <unordered_map>
#include <string>
#include <fstream>
#include <cstring>

#include "../common/util.hpp"
#include "../common/log.hpp"

// 文件版本
namespace ns_model
{
    using namespace ns_util;
    using namespace ns_log;

    // 每一个题目独享的内存细节
    struct Question
    {
        std::string number;  // 题目编号
        std::string title;  // 题目标题
        std::string star;  // 题目难度
        int cpu_commit;  // 运行时间限制S
        int mem_commit;  // 内存占用限制KB

        std::string desc;  // 文件描述
        std::string header;  // 用户预设代码
        std::string tail;  // 测试代码
        std::string test_input;  // 测试用户代码
    };

    class Model
    {
        const std::string questions_path = "./questions/question.list";
    private:
        std::unordered_map<std::string, Question> _map;
    public:
        Model()
        {
            // 题库信息加载入内存
            assert(LoadQuestionlist(questions_path));
        }

        ~Model()
        {}

        // 开始加载题库入内存
        bool LoadQuestionlist(const std::string& path)  // 题库文件questions的路径
        {
            std::ifstream in(path);
            if (!in.is_open())
            {
                // 此时致命错误-影响全部，检查文件路径
                LOG(FATAL) << "题库配置文件未能打开，请检查相关路径或者文件是否存在" << "\n";
                return false;
            }

            std::string line;
            while (std::getline(in, line))
            {
                if (line[0] == '#') continue;  // 注释行跳过
                // 对于配置的一行，我们需要进行字符串分割
                std::vector<std::string> target;
                StringUtil::SpiltString(line, target, " ");
                if (target.size() != 6)
                {
                    // 此时当前配置行存在误输，警告
                    LOG(WARING) << line[0] << "编号配置文件出错" << "\n"; 
                    continue;
                }

                Question q;
                q.number = target[0];
                q.title = target[1];
                q.star = target[2];
                q.cpu_commit = atoi(target[3].c_str());
                q.mem_commit = atoi(target[4].c_str());

                q.desc = FileUtil::ReadFile(target[5] + "/desc.txt", true);
                q.header = FileUtil::ReadFile(target[5] + "/header.cpp", true);
                q.tail = FileUtil::ReadFile(target[5] + "/tail.cpp", true);
                q.test_input = FileUtil::ReadFile(target[5] + "/test_input.cpp", true);
                if (q.desc == "" || q.header == "" || q.tail == "" || q.test_input == "")
                {
                    // 当前记录文件读取失败！或者无记录，那么就无效，不纳入统计
                    LOG(WARING) << "检查编号" << line[0] << "文件目录下文件是否录入成功或者填写信息" << "\n"; 
                    LOG(DEBUG) <<  target[5] << "\n";
                    continue;
                }
                _map[target[0]] = q;  // 保存记录
            }

            in.close();
            LOG(INFO) << "题库录入内存成功！" << "\n";
            return true;
        }

        // 获取当前题目列表信息
        bool GetAllQuestions(std::vector<Question>& v)
        {
            if (_map.empty())
            {
                // 当前内存题目信息为空，获取失败
                LOG(ERROR) << "当前用户获取题目信息失败，内存中不存在题目信息记录" << "\n";
                return false;
            }
            for (auto& s : _map)
            {
                v.push_back(s.second);
            }
            return true;
        }

        // 获取单个题目信息，给我number
        // 已经提供日志差错处理
        bool GetOneQuestion(const std::string& number, Question& q)
        {
            auto it = _map.find(number);
            if (it == _map.end())
            {
                // 提供的number不存在当前题库中
                LOG(ERROR) << "当前用户获取题目信息失败，内存中不存在此题目信息记录" << "\n";
                return false;
            }
            q = it->second;
            return true;
        }
    };
}

#endif